#define PrynInternal
#define PrynImplementation
#include <pryn.h>
#include <string.h>

PrynExport (PrynResult, prynStringNull, (PrynString *result))
{
	if (!result) return PrynResultNullArgument;
	result->pointer = 0;
	result->length = 0;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynStringToZ, (const PrynString *string, PrynState *state, const char **output))
{
	char *copy;
	PrynResult result;

	if (output) *output = 0;
	if (!string || !output) return PrynResultNullArgument;
	if (!string->pointer) return PrynResultSuccess;
	if (string->pointer [string->length] == 0) { *output = string->pointer; return PrynResultSuccess; }
	if ((result = prynAllocate (state, string->length + 1, (void **) &copy)) < 0) return result;
	prynCopyMemory (copy, string->pointer, string->length);
	copy [string->length] = 0;
	*output = copy;

	return PrynResultSuccess;
}

PrynExport (PrynResult, prynStringFrom, (PrynString *result, const char *pointer, uintptr_t length))
{
	if (!result) return PrynResultNullArgument;
	result->pointer = length ? (char *) pointer : 0;
	result->length = pointer ? length : 0;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynStringFromZ, (PrynString *result, const char *pointer))
{
	size_t length = 0;
	
	if (pointer) for ( ; pointer [length]; length ++);
	return prynStringFrom (result, pointer, length);
}

PrynExport (PrynResult, prynStringFromZMaximum, (PrynString *result, const char *pointer, uintptr_t maximumLength))
{
	size_t length = 0;
	
	if (pointer) for ( ; length < maximumLength && pointer [length]; length ++);
	return prynStringFrom (result, pointer, length);
}

PrynExport (PrynResult, prynStringEqual, (const PrynString *a, const PrynString *b))
{
	if (!a || !b) return PrynResultNullArgument;
	if (a->length != b->length) return PrynResultFalse;
	return prynMemoryEqual (a->pointer, b->pointer, a->length);
}

PrynExport (PrynResult, prynStringStartsWith, (const PrynString *string, const PrynString *prefix))
{
	PrynStartFunction ();

	prynAssert (string && prefix, NullArgument);
	if (string->length < prefix->length) prynReturnFalse ();
	prynReturnCall (prynMemoryEqual (string->pointer, prefix->pointer, prefix->length));

	PrynEndFunction ();
}

PrynExport (PrynResult, prynStringSliceToEnd, (PrynString *result, const PrynString *string, uintptr_t offset))
{
	if (result) prynStringNull (result);
	if (!string) return PrynResultNullArgument;
	if (offset >= string->length) return prynStringNull (result);
	return prynStringFrom (result, string->pointer + offset, string->length - offset);
}

PrynExport (PrynResult, prynStringSliceTo, (PrynString *result, const PrynString *source, size_t end))
{
	if (!result || !source) { prynStringNull (result); return PrynResultNullArgument; }
	return prynStringFrom (result, source->pointer, prynMinimum (end, source->length));
}

PrynExport (PrynResult, prynStringJoin, (PrynString *output, PrynState *state, const PrynString *pa, const PrynString *pb))
{
	PrynString a, b, null = {0, 0};
	size_t length;
	char *pointer;

	if (pa && pb) a = *pa, b = *pb;
	if (output) *output = null;
	if (!output || !pa || !pb || !state) return PrynResultNullArgument;

	length = a.length + b.length;
	prynAllocate (state, length + 1, (void **) &pointer);
	if (!pointer) return PrynResultOutOfMemory;
	prynCopyMemory (pointer, a.pointer, a.length);
	prynCopyMemory (pointer + a.length, b.pointer, b.length);
	pointer [length] = 0;

	return prynStringFrom (output, pointer, length);
}

PrynExport (PrynResult, prynStringFree, (PrynString *string, PrynState *state))
{
	if (!string || !state) { prynStringNull (string); return PrynResultNullArgument; }
	if (state && string->pointer)
		prynFree (state, string->pointer);
	return prynStringNull (string);
}

PrynExport (PrynResult, prynStringDuplicate, (PrynString *result, PrynState *state, const PrynString *pstring))
{
	PrynString string;
	char *pointer;
	size_t length;

	if (pstring) string = *pstring;
	prynStringNull (result);
	if (!state || !pstring || !result) return PrynResultNullArgument;

	length = string.length;
	prynAllocate (state, length + 1, (void **) &pointer); // Allocate space for a NUL terminator.
	if (!pointer) return PrynResultOutOfMemory;

	prynCopyMemory (pointer, string.pointer, length);
	pointer [length] = 0; // Put in a NUL terminator for convenience.

	return prynStringFrom (result, pointer, string.length);
}

PrynExport (PrynResult, prynStringOverwrite, (PrynString *string, PrynState *state, const PrynString *overwrite))
{
	PrynStartFunctionHasState ();
	prynAssert (string && state, NullArgument);
	prynCall (prynStringFree (string, state));
	prynCall (prynStringDuplicate (string, state, overwrite));
	PrynEndFunction ();
}

PrynExport (PrynResult, prynStringFreeZ, (const PrynString *string, PrynState *state, const char *zstring))
{
	PrynStartFunctionHasState ();

	prynAssert (string && state, NullArgument);
	if (zstring && zstring != string->pointer)
		prynFree (state, (void *) zstring);

	PrynEndFunction ();
}

PrynExport (PrynResult, prynStringAllocate, (PrynString *output, PrynState *state, size_t length))
{
	PrynStartFunctionHasState ();
	PrynString null = {0, 0};
	void *pointer;

	if (!output) *output = null;
	prynAssert (output && state, NullArgument);

	prynCall (prynAllocateCleared (state, length + 1, &pointer));
	output->pointer = pointer;
	output->length = length;

	PrynEndFunction ();
}